﻿Ext.data.Connection = Ext.extend(Ext.util.Observable, {
    method: 'post',
    url: null,


    disableCaching: true,


    disableCachingParam: '_dc',


    timeout: 30000,

    useDefaultHeader: true,
    defaultPostHeader: 'application/x-www-form-urlencoded; charset=UTF-8',
    useDefaultXhrHeader: true,
    defaultXhrHeader: 'XMLHttpRequest',

    constructor: function (config) {
        config = config || {};
        Ext.apply(this, config);

        this.addEvents(

            'beforerequest',

            'requestcomplete',

            'requestexception'
        );
        this.requests = {};
        Ext.data.Connection.superclass.constructor.call(this);
    },

    //发送请求
    request: function (o) {
        var me = this;
        if (me.fireEvent('beforerequest', me, o) !== false) {

            //params: Object/String/Function 
            //一个对象，它包含作为请求一个URL编码的字符串或一个函数调用，以获得参数的属性。该函数的范围是指定的范围选项。
            var params = o.params,           
                url = o.url || me.url,
                urlParams = o.urlParams,
                extraParams = me.extraParams,
                request, data, headers,
                method, key, xhr;


            if (Ext.isFunction(params)) {
                params = params.call(o.scope || window, o);
            }


            if (Ext.isFunction(url)) {
                url = url.call(o.scope || window, o);
            }


            data = o.rawData || o.xmlData || o.jsonData || null;
            if (o.jsonData && !Ext.isPrimitive(o.jsonData)) {//如果jsonData不是javascript基础类型的数据
                data = Ext.encode(data);
            }


            params = Ext.urlEncode(extraParams, Ext.isObject(params) ? Ext.urlEncode(params) : params);

            urlParams = Ext.isObject(urlParams) ? Ext.urlEncode(urlParams) : urlParams;


            method = (o.method || ((params || data) ? 'POST' : 'GET')).toUpperCase();


            if (method === 'GET' && o.disableCaching !== false && me.disableCaching) {
                url = Ext.urlAppend(url, o.disableCachingParam || me.disableCachingParam + '=' + (new Date().getTime()));
            }


            if ((method == 'GET' || data) && params) {
                url = Ext.urlAppend(url, params);
                params = null;
            }


            if (urlParams) {
                url = Ext.urlAppend(url, urlParams);
            }


            if (o.autoAbort === true || me.autoAbort) {
                me.abort();
            }

            //获取XMLHttpRequest对象
            xhr = this.getXhrInstance();


            xhr.open(method.toUpperCase(), url, true);


            headers = Ext.apply({}, o.headers || {}, me.defaultHeaders || {});
            if (!headers['Content-Type'] && (data || params)) {
                var contentType = me.defaultPostHeader,
                    jsonData = o.jsonData,
                    xmlData = o.xmlData;

                if (data) {
                    if (o.rawData) {
                        contentType = 'text/plain';
                    } else {
                        if (xmlData && Ext.isDefined(xmlData)) {
                            contentType = 'text/xml';
                        } else if (jsonData && Ext.isDefined(jsonData)) {
                            contentType = 'application/json';
                        }
                    }
                }
                headers['Content-Type'] = contentType;
            }
            if (me.useDefaultXhrHeader && !headers['X-Requested-With']) {
                headers['X-Requested-With'] = me.defaultXhrHeader;
            }

            for (key in headers) {
                if (headers.hasOwnProperty(key)) {
                    try {
                        xhr.setRequestHeader(key, headers[key]);
                    }
                    catch (e) {
                        me.fireEvent('exception', key, headers[key]);
                    }
                }
            }


            request = {
                id: ++Ext.data.Connection.requestId,
                xhr: xhr,
                headers: headers,
                options: o,
                timeout: setTimeout(function () {
                    request.timedout = true;
                    me.abort(request);
                }, o.timeout || me.timeout)
            };
            me.requests[request.id] = request;


            xhr.onreadystatechange = Ext.createDelegate(me.onStateChange, me, [request]);


            xhr.send(data || params || null);
            return request;
        } else {
            return o.callback ? o.callback.apply(o.scope, [o, undefined, undefined]) : null;
        }
    },

    //获取XMLHttpRequest对象
    getXhrInstance: function () {
        return new XMLHttpRequest();
    },


    isLoading: function (r) {

        return r && !{ 0: true, 4: true}[r.xhr.readyState];
    },


    abort: function (r) {
        if (r && this.isLoading(r)) {
            if (!request.timedout) {
                request.aborted = true;
            }

            r.xhr.abort();
        }
        else if (!r) {
            var id;
            for (id in this.requests) {
                if (!this.requests.hasOwnProperty(id)) {
                    continue;
                }
                this.abort(this.requests[id]);
            }
        }
    },


    onStateChange: function (r) {
        if (r.xhr.readyState == 4) {
            clearTimeout(r.timeout);
            delete r.timeout;
            this.onComplete(r);
        }
    },


    onComplete: function (r) {
        var status = r.xhr.status,
            options = r.options,
            success = true,
            response;

        if ((status >= 200 && status < 300) || status == 304) {
            response = this.createResponse(r);
            this.fireEvent('requestcomplete', this, response, options);
            if (options.success) {
                if (!options.scope) {
                    options.success(response, options);
                }
                else {
                    options.success.call(options.scope, response, options);
                }
            }
        }
        else {
            success = false;
            switch (status) {
                case 12002:
                case 12029:
                case 12030:
                case 12031:
                case 12152:
                case 13030:
                    response = this.createException(r);
                    break;
                default:
                    response = this.createResponse(r);
            }
            this.fireEvent('requestexception', this, response, options);
            if (options.failure) {
                if (!options.scope) {
                    options.failure(response, options);
                }
                else {
                    options.failure.call(options.scope, response, options);
                }
            }
        }

        if (options.callback) {
            if (!options.scope) {
                options.callback(options, success, response);
            }
            else {
                options.callback.call(options.scope, options, success, response);
            }
        }

        delete this.requests[r.id];
    },


    createResponse: function (r) {
        var xhr = r.xhr,
            headers = {},
            lines = xhr.getAllResponseHeaders().replace(/\r\n/g, '\n').split('\n'),
            count = lines.length,
            line, index, key, value;

        while (count--) {
            line = lines[count];
            index = line.indexOf(':');
            if (index >= 0) {
                key = line.substr(0, index).toLowerCase();
                if (line.charAt(index + 1) == ' ') {
                    ++index;
                }
                headers[key] = line.substr(index + 1);
            }
        }

        delete r.xhr;

        return {
            request: r,
            requestId: r.id,
            status: xhr.status,
            statusText: xhr.statusText,
            getResponseHeader: function (header) { return headers[header.toLowerCase()]; },
            getAllResponseHeaders: function () { return headers; },
            responseText: xhr.responseText,
            responseXML: xhr.responseXML
        };
    },


    createException: function (r) {
        return {
            request: r,
            requestId: r.id,
            status: r.aborted ? -1 : 0,
            statusText: r.aborted ? 'transaction aborted' : 'communication failure',
            aborted: r.aborted,
            timedout: r.timedout
        };
    }
});

Ext.data.Connection.requestId = 0;